""" An event management system that expands upon pygame.event.

    See the "Events" page in the ScrollBack wiki for more information
    about how event management works in ScrollBack. Only certain
    general details will be given here.
    
    TODO: Add support for per-event blocking on responders.
    TODO: ??? Remove support for weak hooks ???
    
    ~~~ Event Type Descriptions ~~~
    
    <Type-Constant> <Attributes>
    
        <Description>
    
    
    CONTROLEVENT    control, ...
        
        Used to control the flow of programs that use an event loop as
        the main loop. Control events may have other attributes
        depending on the value of the `control` attribute. (See below.)
    
    
    WIDGETEVENT     action, id, tag, widget, ...
    
        Used to indicate that a widget has done something, or that
        something has been done to a widget. The `action` indicates
        what happened, `id` is the ID of the widget that was effected
        (or that affected the event), `tag` is the tag of the widget,
        and `widget` is the widget itself. Other attributes may exist
        depending on the action. (See below.)
    
    
    UIEVENT         uitype, ui, id
    
        Posted by Inputs when their respective hardware control events
        occur. (See the `sbak.ui` module.)
    
    ~~~ Control Events ~~~
    
    Control events have different purposes and different attributes
    depending on the `control` value, or "subtype," of each control
    event. These are described below:
    
    <Control>       <Attributes>
    
        <Description>
        
    
    CE_CYCLE        (none)
    
        Indicates the beginning of a main loop cycle. Exactly one
        CE_CYCLE should be posted at the beginning of each main loop
        cycle before any other events. Immediately following this
        event should be the system events--those that are posted by the
        operating system, such as keyboard, mouse, and SDL window
        events.
    
    
    CE_UPDATE       (none)
    
        Indicates the internal-updates portion of the main event cycle,
        in which each object takes care of object-specific things that
        need to be done regularly after each wave of system events.
    
    
    CE_DISPLAY      surface, offset, region
    
        Indicates that everything needs to display itself. The event
        indicates the surface where things should display (`surface`),
        an offset within the surface that should be treated as drawing
        origin within the surface (`offset`), and a restricted region
        within the surface that is being displayed (`region`). All
        these values are optional; If None is given for any value, a
        default is used. For `surface`, the default is the screen
        surface. For `offset`, the default is (0,0), meaning no offset.
        Lastly, for region, the entire region of the surface is used.
    
    
    CE_REFRESH      regions
    
        Indicates that some regions of the screen should be refreshed.
        The `regions` attribute should be a sequence of rectangular
        regions or else None. If None is given as the region, the
        entire screen is assumed.
    
    
    <WRONG as of 2009-09-11>
        For games, each main loop cycle consists of one of each of these
        four control events occuring in the order listed above. (See the
        `sbak.app.Game` class for more information.)
    
    
        For GUI applications, only the CE_CYCLE control event is guarunteed
        to occur once per main loop cycle. However, the other control
        events may be triggered, possibly in multiple, as a result of
        various actions, such as moving or resizing a window, clicking a
        button, etc.
    </WRONG>
    
    
    ~~~ Widget Events ~~~
    
    Widget events corespond to widget actions, such as button clicks or
    scrollbar scrolling. The type of action depends on the value of the
    `action` attribute, which is given along with the widget, its ID,
    its tag, and possibly other values depending on the action:
    
    <Action>        <Attributes>
    
    WE_CLICKED      The mouse was pressed and released on the same
                    widget. Posted by buttons when the user presses
                    and releases them.
"""

from functools import partial
from weakref import WeakKeyDictionary, WeakValueDictionary
from Queue import Queue, Empty as QueueEmpty
from operator import attrgetter

import pygame
from pygame.event import get, post, Event, pump, poll
from pygame.key import name as get_key_name
from pygame import (
    NUMEVENTS,
    NOEVENT,
    USEREVENT,
    QUIT,
    ACTIVEEVENT,
    KEYDOWN,
    KEYUP,
    MOUSEMOTION,
    MOUSEBUTTONUP,
    MOUSEBUTTONDOWN,
    JOYAXISMOTION,
    JOYBALLMOTION,
    JOYHATMOTION,
    JOYBUTTONUP,
    JOYBUTTONDOWN,
    VIDEORESIZE,
    VIDEOEXPOSE
)

import sbak.base
from sbak import error
from sbak.tree import Leaf, Branch


# Constants


# Control events
CONTROLEVENT = USEREVENT
CE_CYCLE = 'ce_cycle' # Beginning of cycle
CE_UPDATE = 'ce_update' # World update; one per world
CE_COLLISION = 'ce_collision' # Collision between two objects
CE_DISPLAY = 'ce_display' # Scenes are being displayed on the screen.
CE_REFRESH = 'ce_refresh' # The screen is about to be refreshed

# Input events
UIEVENT = USEREVENT+1
UI_BUTTONUP = 'ui_buttonup'
UI_BUTTONDOWN = 'ui_buttondown'

# Widget events
WIDGETEVENT = USEREVENT+2
WE_CLICKED = 'we_clicked'

__all__ = (
    'CONTROLEVENT',
    'CE_CYCLE',
    'CE_UPDATE', 
    'CE_COLLISION',
    'CE_DISPLAY',
    'CE_REFRESH',
    
    'UIEVENT',
    'UI_BUTTONUP',
    'UI_BUTTONDOWN'
    
    'WIDGETEVENT',
    'WE_CLICKED',
    
    'Event',
    
    'Responder',
    'Forwarder',
    'EventManager',
    'AbstractEventQueueManager',
    'SysEventQueueManager',
    'EventQueueManager',
    'EQM',
    'SysEQM',
    'Hook',
    
    'post',
    'pump',
    'get',
    
    'init',
    'post_ex',
    'get_name',
    'get_key_num',
    'get_key_name'
)
 
# Used to map string IDs to event IDs
_namee = {  
    'quit':QUIT,
    'activeevent':ACTIVEEVENT,
    'keydown':KEYDOWN,
    'keyup':KEYUP,
    'mousemotion':MOUSEMOTION,
    'mousebuttonup':MOUSEBUTTONUP,
    'mousebuttondown':MOUSEBUTTONDOWN,
    'joyaxismotion':JOYAXISMOTION,
    'joyballmotion':JOYBALLMOTION,
    'joyhatmotion':JOYHATMOTION,
    'joybuttonup':JOYBUTTONUP,
    'joybuttondown':JOYBUTTONDOWN,
    'videoresize':VIDEORESIZE,
    'videoexpose':VIDEOEXPOSE,
    'controlevent':CONTROLEVENT,
    'uievent':UIEVENT,
    'widgetevent':WIDGETEVENT
}

# Also map event IDs back to names.
_ename = {}
for k,v in _namee.iteritems():
    _ename[v] = k

# Do the same for event subtypes
_namesube = {
    'cycle' : CE_CYCLE,
    'update' : CE_UPDATE,
    'collision' : CE_COLLISION,
    'display' : CE_DISPLAY,
    'refresh' : CE_REFRESH,
    'cycle' : CE_CYCLE,
    'buttonup' : UI_BUTTONUP,
    'buttondown' : UI_BUTTONDOWN,
    'clicked' : WE_CLICKED
}
_subename = {}
for k,v in _namesube.iteritems():
    _subename[v] = k


# Global defaults


# Determines whether or not the module has been initialized yet
_init = False


# Functions


def init():
    """ Initializes the event system.
    
        This should be called before using the `event` module's other
        functions and classes, otherwise "bad things will happen."
        
        Before you can call this you must make sure that pygame.display
        has been initialized or else sbak.error.Error is raised.
        
        If you call sbak.init, `pygame.display` and `sbak.event` are
        both initialized properly.
    """
    global _init, _key_nums, _noevent
    
    if not pygame.display.get_init():
        raise error.Error("pygame.display not initialized.")

    if not _init:
        _init = True
        
        # Create and optimize the key name dictionary.
        # (This does not work until pygame.display is initialized.)
        _key_nums = {}
        for c in xrange(323):
            name = pygame.key.name(c)
            _key_nums[name] = c
        for name in _key_nums.keys():
            if 'world' in name or 'unknown key' in name:
                del _key_nums[name]
        
        # Create the default NOEVENT event
        # (Used as the return value for EventManager.poll when no event
        # is on the queue)
        _noevent = Event(NOEVENT)

def post_ex(type ,state=None, **kwstate):
    """ Puts a new event of the specified kind onto the system queue.
        
        type:   Pass one of...
                * A Pygame event constant, ex. KEYUP, KEYDOWN, etc.
                * A ScrollBack event constant, ex. CONTROLEVENT
                * A Scrollback event subtype constant, ex. CE_UPDATE
                * A string equivalent to one of the above, ex. 'keyup',
                  'controlevent', 'update', 'axis', ...
                NOTE: If you pass a subtype value, this implies that
                you are posting an event of the proper type associated
                with the sub type and with the subtype given. (For
                example, if you pass `type = CE_UPDATE`, it is
                interpreted as
                `type = CONTROLEVENT, control = CE_UPDATE`.)
        
        state:  Optionally, pass a dictionary mapping event attribute
                names to values. Make sure to pass the correct
                attributes for he type you are passing.*
        
        **kwstate:
                Alternatively, you can pass a series of keyword
                arguments instead of a dictionary. The effect is
                the same.
                
        This will create a new event with the given description and
        post it to the system queue using `pygame.event.post`, which is
        also available as `sbak.event.post`.
        
        * If the keyword "key" is present and its value is a string, the
        value will be converted to a key number using get_key_num().
    """
    state = dict(state or kwstate)
    
    type = _fix_state(type, state)
    
    # Create and post the event
    post(Event(type, state))

def get_name(e):
    """ Returns the name of an event type.
    
        Pass a Pygame or ScrollBack event constant.
        
        Returns a string, the name of the event.
    """
    return _ename[e]

def get_num(name):
    """ Converts an event name to an event type constant.
    
        Pass a string. The function will try to determine which
        constant is needed and return it. If the constant cannot be
        determined then a ValueError is raised.
        
        or
        
        Pass an integer. It will be returned unchanged.
        
        Passing any other type value results in a TypeError.
    """
    if isinstance(name, int):
        return name
    if not isinstance(name,str):
        raise TypeError("input must be str or int, got %s" % repr(name))
        
    fixed = name.lower().strip()
    try:
        return _enum[fixed]
    except KeyError:
        raise ValueError("cannot determine constant for %s" % repr(name))

def get_key_num(key):
    """ Returns the number of the keyboard key with the given name.
        
        This function can be used as an alternative to the Pygame
        K_* constants. Simply pass the name of the key as a string
        (str and unicode are both accepted) and the value of the
        corresponding key (which is equal to the key's K_* constant)
        will be returned.
        
        This function is used throughout this module to convert key
        names to key numbers.
        
        Raises TypeError if the given value is not a string, or
        ValueError if the string could not be translated to a keyboard
        key code.
    """
    if not isinstance(key, basestring):
        raise TypeError("key must be str or unicode; got %s"%key)
    try:
        return _key_nums[key.strip().lower()]
    except KeyError:
        raise ValueError("Could not translate \"%s\" to a keyboard key." % key)   

def _fix_state(type, state):
    """ Internal function, Do not use.
    
        This function is part of the implementation and may change or
        be removed later. Don't rely on it.
        
        Converts type and state values given in as "lazy strings" into
        their proper values. The new type is returned, and state may
        be altered. Both, either, or neither of type and state may
        change depending on given values.
    """
    
    # Convert string event IDs to numeric IDs
    if isinstance(type,str):
        # If the type passed is a control event subtype, change type
        # to a control event and subtype to given type.
        if type[:3] == 'ce_':
            state['control'] = type
            type = CONTROLEVENT
        
        # If the type passed is a ui event subtype, change type
        # to a ui event and subtype to given type.
        elif type[:3] == 'ui_':
            state['uitype'] = type
            type = UIEVENT
            
        # If the type passed is a widget event subtype, change type
        # to a ui event and subtype to given type.
        elif type[:3] == 'we_':
            state['action'] = type
            type = WIDGETEVENT
        
        # If neither of the above are true, just convert the string
        # to an event constant.
        else:
            type = _namee[type.strip().lower()]
    
    # Convert sub types
    
    # If the `type` indicates a keyboard event,
    # and a specific key is indicated,
    # and that key is given as a string,
    # then convert the string to a keycode.
    if ( type in (KEYUP, KEYDOWN)
     and 'key' in state
     and isinstance(state['key'], basestring) ):
            state['key'] = get_key_num(state['key'])
    
    # If the type indicates a control event, UI event, or widget event
    elif type in (CONTROLEVENT, UIEVENT, WIDGETEVENT):
        # Get the name of the state subtype attribute that needs to be conv'd.
        subtname = {CONTROLEVENT:'control', UIEVENT:'uitype',
                    WIDGETEVENT:'action'}[type]
        
        # If a subtype was actually given,
        if subtname in state:
            # Assuming the subtype is a string (it should always be a string),
            # format it for lowercase and no whitespace padding.
            subtype = state[subtname].strip().lower()
            
            # If the subtype is one that is recognized as a 'lazy string',
            # convert it to a 'proper string'.
            if subtype in _namesube:
                state[subtname] = _namesube[subtype]
            
    return type


def handler(etype, priority = None, state = (), **kwstate):
    """ Returns a decorator for using a method as an event handler.
    
        decorator = handler(etype, priority = 0, state=(),
                                    **kwstate)
        
        ~~~ Arguments ~~~
        
        etype:  Specify a valid event type constant or name as a case-
                insensitive string. This can be the name of a Pygame
                system event, or a control event, or widget event, and
                so on. The decorator created will mark the decorated
                method to be hooked with this event type.
        
        priority:
                Priority of the event handler. Event handlers with
                lower priority values will be triggered before event
                handlers with the same type and state that have higher
                priority values. If None is given, priority is not
                determined until a Responder hooks the handler, at
                which point it will be given a priority value that is
                equal to 1 + the priority of the highest-priority hook.
        
        state:  Optionally specify a mapping (or anything that can be
                converted to a dict). This is used to indicate specific
                state values that events must have in order to trigger
                the hooked event handler.
        
        **kwstate:
                Instead of passing a mapping for state, keyword
                arguments may be used. If both state and keyword
                arguments are used, keyword values take precedence over
                mapping values.
        
        ~~~ Description ~~~
        
        This method is designed to dynamically create method decorators
        that can mark methods in a class definition of a Responder-
        based class as event handlers. When an instance of such a class
        is created, the Responder class's initializer will
        automatically hook all methods of the class that were marked in
        the class definition.
        
        NOTE that the `handler` function itself is NOT meant to
        be used as a decorator, and doing so will cause methods to be
        replaced with another, fairly useless method--not what you
        normally want to happen. However, no error will occur directly,
        so a bug resulting from incorrect usage of `handler` may
        be difficult to find.
        
        Technical (implementation) details:
        
        The decorator functions returned by `handler` will return
        the same methods that go in, but will modify them slightly by
        adding a few attributes that do not change the behavior of the
        methods. These extra attributes are used by the ResponderM
        metaclass to identify methods as event handlers when creating
        new classes. No other changes are made to the methods.
        
        ~~~ Basic usage ~~~
    
        1.) Just the event type specified, no state:
    
            class MyResponder(Responder)
                @handler(ETYPE)
                def handle_etype(self, e)
                    ...
        
        2.) State specified as dict:
    
            class MyResponder(Responder):
                @handler(ETYPE,
                    {'state1' : value, 'state2' : value2, ...})
                def handle_etype(self, e):
                   ...
        
        3.) State specified as keyword arguments:
        
            class MyResponder(Responder):
                @handler(ETYPE, state1 = value,
                                state2 = value, ...)
                def handle_etype(self, e):
                    ...
            
        4.) INCORRECT USAGE - You don't normally want to do this:
        
            class MyResponder(Responder):
                @handler
                def handle_etype(self, e):
                    # This method NEVER gets executed because the
                    # `handler` function effectively "deleted"
                    # it.
                    ...
    """
    state = dict(state).update(kwstate)
    etype = _fix_state(etype, state)
    
    def modify_handler(handler):
        handler.event_type = etype
        handler.event_state = state
        handler.priority = priority
        return handler
    
    return modify_handler


# Classes

dict_ = dict
class ResponderM(Leaf.__metaclass__):
    """ Metaclass for the Responder class.
    
        This metaclass allows Responder and its subclasses to hook
        methods of the class which were marked using `handler`
        decorators to be collected into a list that is stored as a
        class attribute. Methods in this list are hooked when instances
        of the class are created.
    """
    def __init__(cls, name, bases, dict):
        super(ResponderM, cls).__init__(name, bases, dict)
        
        # Generate the handlers set.
        
        # First, copy the dict of handlers in superclasses, if present
        if hasattr(cls, 'event_handlers'):
            handlers = dict_(cls.event_handlers)
        
        # If not present, create a new dict
        else:
            handlers = {}
        
        # Override handlers from previous classes with those from the new
        for aname in dir(cls):
            attr = getattr(cls, aname)
            
            # If the current attribute has an attribute 'event_type',
            # assume this is an event handler and store it. Replace
            # any entry of the same name from the previous class.
            if hasattr(attr, 'event_type'):
                handlers[aname] = attr
        
        cls.event_handlers = handlers

class Responder(Leaf):
    """ Responds to events by triggering hooked event handlers.
    
        A responder can be used to attach event handlers to event
        descriptions using hooks. All handlers hooked to descriptions
        can then be called by triggering hooks that match a description
        given in the act of triggering. Any number of hooks may be
        created for any number of combinations of handlers/event
        descriptions.
        
        ~~~ Class Attributes ~~~
        
        event_handlers:
            
            A dict that maps the names of class-level event handler
            methods to the methods themselves. Class-level event
            handler methods are those that were decorated using the
            `sbak.event.handler` function. (Please see that method's
            documentation for the correct usage as it is not what some
            people may think.)
            
            This attribute is dynamically generated exactly once for
            each subclass of Responder that is defined. This means that
            each subclass of Responder has its own `event_handlers`,
            although this will be initialized with the same items as
            its parent classes at the time the subclass is created.
            
            Methods in this dict MUST have certain special attributes
            in order for the class to work at all. The Responder
            constructor relies on these attributes, so if they are not
            present, an AttributeError will occur if an attempt is
            made to create a responder. (See "Handler Attributes" below
            for more information.)
        
        ~~~ Instance Attributes ~~~
        
        NOTE: Members prefixed with _underscore are considered to be
        "private" and should not be modified directly.
        
        blocked:    A boolean flag that indicates whether or not the
                    responder is "blocked". Blocked responders do not
                    respond to being notified of events. (However,
                    events can still be triggered by calling the
                    Responder.trigger() method directly instead of
                    indirectly through .notify().)
        
        _hooks:     A mapping of event type IDNs to
                    WeakKeyDictionaries that hold the hooks bound to
                    event descriptions with types that match the IDNs.
                    The hooks are stored as keys in the weakdicts; the
                    values are unused and are all set to None.
        
        _hooks_strongref:
                    A set containing strong references to hooks. All
                    hooks appear in the _hooks attribute, but only
                    hooks added to the responder strongly will appear
                    in this set. When a 
        
        _hipri:     The highest priority number for any hook yet
                    encountered by the Responder. When a hook is
                    created by the responder and no priority is given,
                    the priority of the hook will be one higher than
                    _hipri. The value is updated whenever a hook of
                    higher priority is added to the responder.
        
        ~~~ Class-Level Event Handler Method Attributes ~~~
        
        FIXME:  Somebody please come up with a better, shorter name for these methods!
        
        Class-level event handler methods (CLEHMs) have extra
        attributes that other methods in a class don't normally have.
        Normally, you don't need to worry about these. However, if for
        whatever reason you choose not to use the `handler` function,
        you can "mark" you handlers manually using these attributes:
        
        event_type:     The type of event to which the handler
                        responds.
        
        event_state:    A mapping of the event's attribute names to
                        values that the corresponding event attributes
                        must be equal to besides the type.
        
        priority:       The default priority of the handler. If None,
                        priority it determined when the handler is
                        hooked.
        
        Note that these attributes are only required for class-level
        handlers. Handlers that are hooked using the `Responder.hook`
        method don't need any special attributes. (They still need to
        have the proper event handler signature, however.)
        
    """
    
    __metaclass__ = ResponderM
    
    def __init__(self, id = None):
        Leaf.__init__(self, id=id)
        # Create a mapping from hook/event type numbers to hook collections.
        self._hooks = {}
        for t in xrange(NUMEVENTS):
            self._hooks[t] = WeakKeyDictionary()
        self._hooks_strongref = set()
        
        # Highest priority hook ever recorded for this instance
        self._hipri = 0
        
        # Whether or not this instance is "blocked" from events
        self.blocked = False
        
        # Hook all event handlers defined by the class
        for handler in self.event_handlers.values():
            # Get a bound version of the handler
            bound_handler = handler.__get__(self)
            
            # Hook it
            self.hook(bound_handler, handler.event_type, handler.priority,
                      handler.event_state)
    
    def add_hook(self, hook, weak=False):
        """ Registers (adds) an unregistered hook to the manager.
            
            hook:   A valid Hook or Hook-like object.
            
            weak:   If nonzero, the hook will only be weakly
                    referenced by the event manager.
            
            If `weak` is true, it is the programmer's responsibility
            to make sure that at least one strong reference to the hook
            exists outside of the event manager because the manager
            only stores a weak reference to the registered hook. After
            there are no more strong references to a hook, it is
            automatically removed from the event manager and will no
            longer be called in response to events.
            
            Multiple simultaneous registrations of the the same hook
            are not allowed. Attempting to register a hook that is
            already registered will result in a ValueError being
            raised. Attempting to register a hook that is registered
            with a different EventManager may cause sbak.error.Error
            to occur.
        """
        # Localize hook lists (for speed)
        hooks = self._hooks
        
        # A Responder object stores a dictionary whose keys are event type
        # handlers and whose values are WeakKeyDictionary objects. Each key of
        # each WeakKeyDict is a Hook object, and each value of each WeakKeyDict
        # is None. In this way, each Responder object has a place to
        # store weak references to Hook objects.
        #
        # This setup is less than ideal, to say the least. First of all, it
        # does not keep hooks sorted, meaning that they have to be sorted each
        # time they are triggered. Second, it is an inefficient and unintended
        # use of the weakref system. Nonetheless, it is the fastest way to
        # achieve weak referencing to hooks, so until a better way of doing
        # things is discovered, this is what I'll use.
        
        # If there is no WeakKeyDictionary for the hook/event type, create one.
        if hook.type not in hooks:
            hooks[hook.type] = WeakKeyDictionary()
            
        # Don't allow for the same hook to be registered twice.
        if hook._rsp is not None:
            raise ValueError("Hook %s already registered." % hook)
        
        # Add the hook to the WeakKeyDictionary for its type.
        hook._set_rsp(self)
        hooks[hook.type][hook] = None
        
        # Unless the hook is weak, store a strong to it in a set.
        if not weak:
            self._hooks_strongref.add(hook)
        
        # If the hook's priority is higher than the highest priority, make
        # the given value the new highest.
        if hook.priority > self._hipri:
            self._hipri = hook.priority
    
    def hook(self, callback, type, priority=None, state=None, **kwstate):
        """ Creates a new hook and adds it to the Responder.
        
            callback:   Pass any callable object. This will be called
                        when the hook is triggered and will be passed the
                        hook object as its only parameter.
            
            type:       Pass the event name as a string (case insensitive)
                        or the numeric ID of the event (one of the Pygame
                        event constants).
            
            priority:   Determines order of hook triggerring. Pass a
                        number, preferably an integer. (Negatives are
                        fine.) If None is passed, the highest unused
                        priority integer value is assumed.
                        Default None.
            
            state:      Pass a dictionary with additional values of the
                        event that must be equal to the corrsponsing values
                        of the dictionary for the callback to be called.
                        
                        NOTE: Using keys for which a particular event type
                        does not have corresponding attributes will cause
                        an AttributeError to occur when the
                        event occurs.
            
            Returns a Hook object.
        """
        
        # Default priority is the highest priority.
        if priority is None:
            priority = self._hipri + 1
        
        # Create a new hook.    
        hook = Hook(callback, type, priority, state, **kwstate)
        
        # Add the hook
        self.add_hook(hook)
        
        # Return the hook
        return hook
    
    def hook_weak(self, callback, type, priority=None, state=None, **kwstate):
        """ Creates a new hook and adds it to the Responder weakly.
            
            This is exactly the same as `Responder.hook`, except
            that the new hook is only weakly referenced by the event
            manager and not strongly referenced. This means that it is
            up to the programmer to make sure that there is at least
            one (strong) reference to the new hook after it is created,
            otherwise it will disappear immediately.
            
            Returns a Hook object.
        """
        
        # Default priority is the highest priority.
        if priority is None:
            priority = self._hipri + 1
        
        # Create a new hook.    
        hook = Hook(callback, type, priority, state, **kwstate)
        
        # Add the hook
        self.add_hook(hook, weak=True)
        
        # Return the hook
        return hook
    
    def hook_bound(self, callback, type, priority=None, state=None, **kwstate):
        """ Binds a callback to the responder, then hooks the callback.
        
            This works just like `Responder.hook` except that the
            callback will be given two arguments when called instead
            of just one. The first argument is the responder that
            hooked the callback, and the second is the event that
            caused the callback's hook to be triggered. For example:
            
            >>> def handle_update( r, e ):
            ...     print "Responder", r, "handled event", e
            >>> 
            >>> hook = my_responder.hook_bound(handle_update, 'update')
            
            This method uses Python standard `functools.partial`
            objects to bind callbacks to event handlers.
        """
        p = partial(callback, self)
        return self.hook(p, type, priority, state, **kwstate)
    
    def _unhook_hook(self, hook):
        try:
            del self._hooks[hook.type][hook]
        except KeyError:
            raise ValueError("%r is not hooked in responder %r." % (hook,self))
        self._hooks_strongref.discard(hook)
    
    # Used as a key sorting function in event hook loops.
    _kf = attrgetter('priority')
    
    def unhook_handler(self, callback, type):
        """ Unhooks an event handler.
        
            callback:   Specify a callback (event handler) to remove.
                        The first hook with a callback equal to this
                        one will be removed.
        
            type:       Specify an event type constant or name. The
                        corresponding hook list will be searched for
                        the given callback.
           
           This method can be used to unhook a callable when the hook
           created for the callable is not available or is unknown.
           Hook callbacks are tested for equality, not identity, so
           you should be able to pass a bound method as the callback
           and have the appropriate hook removed even if the callable
           and the actual hooked callback are not the same object.
           
           This method also works with the bound handlers/callbacks
           created with the `Responder.bind` method such that you can
           unhook bound callbacks by passing the callback that was
           bound to the responder.
        """
        type = get_num(type)
        for h in self._hooks[type]:
            # Get the handler for partial callbacks
            try:
                handler = h.callback.func
            # Get the handler for regular callbacks
            except AttributeError:
                handler = h.callback
            if handler == callback:
                h.remove()
                return
    
    def trigger(self, e):
        """ Triggers the hooks for the given event object.
        
            e:    Pass a valid Event or Event-like object.
            
            This will trigger all hooks whose descriptions match the
            given event object. Hooks will be triggered in order from
            lowest to highest priority value.
            
            If any callback of any hook returns true (nonzero), no
            more hooks will be triggered, effectively ending the
            call to .trigger().
            
            NOTE: A KeyError may occur if you attempt to trigger an
            event with a type unknown to ScrollBack for which there
            have never been any registered hooks.
            
            Returns the value of the first hook to return a true
            value, otherwise returns False.
        """
        
        state = e.dict
        
        # Iterate over the hooks in the given event type's list
        for hook in sorted(self._hooks[e.type].keys(), key = self._kf):
            # Verify that the event's state matches the hook's state.
            for k,v in list(hook.state.iteritems()):
                # If an attribute does not match, don't trigger this hook.
                if state[k] != v:
                    break
                
            # If all attributes match, trigger the hook.
            else:
                # If the hook returns nonzero, quit here and return the
                # value of the hook.
                ret = hook(e)
                if ret:
                    return ret
        
        # Default return is False.
        return False
    
    def trigger_ex(self, type, state = None, **kwstate):
        """ Triggers the hooks for the given event description.
        
            mgr.trigger(type) -> stop
            mgr.trigger(type, state) -> stop
            mgr.trigger(type, attr=val, attr=val, ...) -> stop
        
            type:    Pass an event type constant or event name.
            
            state:  Pass a mapping of state attribute names to state
                    attribute values.
            
            **kwstate:
                    Alternatively, pass one or more keyword arguments
                    instead of a state mapping.
        
            A single Event object will be created with the type and
            state given. Each matching hook will be called with the
            event as the hook's argument.
            
            If any callback of any hook returns true (nonzero), no
            more hooks will be triggered, effectively ending the
            call to .trigger_ex().
            
            NOTE: A KeyError may occur if you attempt to trigger an
            event with a type unknown to ScrollBack for which there
            have never been any registered hooks.
            
            Returns False if none of the called hooks return True.
            Returns True if any of the called hooks return True.
        """
        
        # Combine state and kwstate. Null state is converted to empty dict.
        state = dict(state or {})
        state.update(kwstate)
        
        # Convert named types and states to the correct values.
        type = _fix_state(type, state)
        
        # Create the event that the callbacks are expecting.
        e = Event(type, state)
        
        return self.trigger(e)
        
    def notify(self, e):
        """ Notify this responder of an event's occurence.
        
            e:    Pass an Event or Event-bound Hook.
            
            The default behavior of a hook when notified of an event is
            simply to trigger all handlers for the event by passing it
            along to self.trigger(), unless the responder is blocked
            (i.e. the .blocked member is nonzero), in which case
            nothing happens.
            
            This behavior is overridden by subclasses, such as
            EventManager.
            
            Returns whatever self.trigger(e) returns, unless blocked,
            in which case None is returned.
        """
        if not self.blocked:
            return self.trigger(e)
    
    def kill(self):
        """ Kills the responder by orphanizing and unhooking all hooks.
        
            This will unhook all hooks created for the responder, and
            will remove the responder from its parent. Only call this
            when you do not intend to use the responder anymore.
        """
        Leaf.kill(self)
        for L in self._hooks:
            for h in L:
                h.remove()

class ResponderBranch(Branch, Responder):
    """ A combination of `sbak.tree.Branch` and `sbak.event.Responder`.
    
        rb = ResponderBranch([child, child, ...], id = None)
        
        ~~~ Description ~~~
       
        This class is basically what the name suggests: It is a node
        and a responder put together. It doesn't provide any special
        functionality for being notified of events as does Forwarder
        or EventManager or anything else. In fact, the two following
        class definitions are basically equivalent:
        
        >>> class MyRB(Branch, Responder):
        ...     def __init__(self, children=())
        ...         Responder.__init__(self)
        ...         Branch.__init__(self, children)
        >>>
        >>> class MyBN(ResponderBranch):
        ...     def __init__(self, children=())
        ...         BranchBranch.__init__(self, children))
        
        This class only exists because several widgets in the
        `sbak.widget` module need to inherit from both Responder and
        Branch (but not Forwarder or EventManager) and I thought it
        would be a good idea to have a shortcut for doing so.
    """
    def __init__(self, children=(), id = None):
        Responder.__init__(self, id=id)
        Branch.__init__(self, children, id=id)

class Forwarder(Branch):
    """ Forwards received events to children.
    
        fw = Forwarder()
        fw = Forwarder([ child, child, ... ])
    
        ~~~ Description ~~~
        
        A forwarder is a node type that will indiscriminately pass
        any event it receives via its .notify method to each child
        using the child's .notify method.
        
        Currently the Forwarder class does not support any way to
        control which types of event are forwarded. If this kind of
        control is needed, it may be better to use a Responder that
        has event handlers which pass the event received on to
        children.
    
        ~~~ Attributes ~~~
        
        blocked:    If True, the forwarder will not forward ANY events.
    
    """
    
    __slots__ = ['blocked']
    
    def __init__(self, children=(), id = None):
        Branch.__init__(self, children, id=id)
        self.blocked = False
    
    def notify(self, e):
        """ Forwards the given event to each unblocked child.
        
            This forwards the given event to each of the
            forwarder's children in order. This is done by
            calling `child.notify(e)` for each child. If any
            child.notify() returns nonzero, the parent stops forwarding
            and its own .notify() returns nonzero as well. Otherwise,
            forwarding continues until every child receives the event,
            after which the parent forwarder.notify() returns false.
            
            If the forwarder is blocked, this method does nothing and
            returns None.
        """
        if not self.blocked:
            for child in self[:]:
                ret = child.notify(e)
                if ret:
                        return ret
            return False

class EventManager(Forwarder, Responder):
    """ Combines the functionality of responders and forwarders.
    
        mgr = EventManager([ child, child, ...], id = None)
    
        Instances of this class have the abilities to create hooks,
        to receive notification of events, to trigger hooks, to attach
        children and/or change parent node, and to forward event
        notifications on to its children.
        
        When an event manager is notified of an event, and assuming the
        manager is not blocked, it triggers its own hooks for the event
        and then forwards the notification on to each of its children
        in order. Therefore, it effectively combines the .notify()
        behavior for both Responder and Forwarder.
        
        Additionally, EventManager has the following special behavior
        for hooks that return -1. Normally, a false value indicates
        that an event should continue being forwarded, and a true
        value indicates that the event should stop being forwarded, but
        EventManager ammends this rule so that any hook implemented
        directly on the EventManager itself (rather than its children)
        that returns -1 will NOT stop the event from being forwarded to
        the EM's children, but WILL stop the event from being forwarded
        to the other siblings of the EM. This allows the EM to restrict
        some events to its children, but not to the children of other
        EM's at the same level in the event tree.
        
        ~~~ Class Hierarchy ~~~
        
        Methods are resolved in the following order:
        
        sbak.event.EventManager
            sbak.event.Forwarder
                sbak.tree.Branch
                    sbak.tree.Leaf
            sbak.event.Responder
                sbak.tree.Leaf
    """

    def __init__(self, children=(), id = None):
        Responder.__init__(self, id = id)
        Forwarder.__init__(self, children, id = id)
    
    def notify(self, e):
        """ Notfies self and children of an event.
        
            e:    The event to notify with.
            
            This will call `Responder.notify(self, e)` to trigger
            event handlers implemented directly on the EM. If that
            does not return any nonzero value (other than -1) then the
            event will be forwarded to each child using
            `Forwarder.notify(self, e)`. If that does not return any
            nonzero value (INCLUDING -1) then the resulting value is
            returned. Otherwise, the value returned by
            `Responder.notify` is returned.
            
            The general idea is that if any hook of the EM itself 
            returns -1, the event will still be forwarded to children,
            but will not be allowed to propagate to siblings of the EM.
            Essentially, -1 is treated as a "partial forward" signal.
        """
        
        # If own hooks do not cause a termination signal (ie. nonzero return
        # other than -1) then forward the event to children
        ret = Responder.notify(self, e)
        if ret == -1 or not ret:
            # If forwarding to children does not cause a termination signal
            # (ie. any nonzero value, including -1) then return that value.
            ret2 = Forwarder.notify(self, e)
            if ret2:
                return ret2
        
        # If forwarding to children did not cause a termination signal,
        # return the value of own hook calling.
        return ret

class AbstractEventQueueManager(EventManager):
    """ An abstract base for event queue managers.
    
        ~~~ Usage ~~~
    
        class FooEQM(AbstractEventQueueManager): ...
        
        or
        
        class FooEQM(AbstractEQM): ...
        
        ~~~ Description ~~~
    
        NOTE: This class does NOT relate to Python's `abc` (Abstract
        Base Class) module in any way.
    
        This class implements the basic API (and some features) of an
        event queue manager (EQM), such as posting, polling, getting,
        and dispatching. It also derives from EventManager.
        
        The only thing this class lacks are proper implementations of
        the .post, .poll, and .get methods. These are filled in by
        the common EQM classes, `EQM` and `SysEQM`, and must have the
        following basic formats: (NOTE: "->" means "returns")
        
        .post(e)  ->  None
        .poll(e)  ->  <Event object>
        .get()  ->  [<Event obj>, <Event obj>, ...]
    
        ~~~ Class Hierarchy ~~~
        
        Methods are resolved in the following order:
        
        sbak.event.AbstractEventQueueManager
            sbak.event.EventManager
                sbak.event.Forwarder
                    sbak.tree.Branch
                        sbak.tree.Leaf
                sbak.event.Responder
                    sbak.tree.Leaf
    """
    
    def dispatch(self):
        """ Self-notifies for each event in the queue.
        
            This will cause the EQM to notify itself of each event that
            appears in the EQM's queue in order of appearence. This
            will, in turn, notify each 
        
            NOTE: This method makes use of the .get method of the
            calling instance. This may make EQM.update slower
            since it will force Queue.Queue.get_nowait() to be called
            many times. However, use from a SysEQM object does
            not suffer from this problem because its .get method is
            actually just a wrapper around pygame.event.get.
        """
        
        # Get localized access to self.notify;
        # this speeds things up a bit.
        notify = self.notify
        
        for e in self.get():
            notify(e)
    
    def dispatch_repeat(self):
        """ Keeps triggering hooks until the queue is empty.
        
            This method is similar to the dispatch() method.
            However, whereas a normal dispatch gets all events
            that have occurred, self-notifies, and then quits, this
            method keeps checking and notifying using the .poll method
            until the event queue is empty. This means that, for as
            long as events contine to be posted, this method will never
            return.
        """
        
        # Get localized access to self.notify and self.poll;
        # this speeds things up a bit.
        notify = self.notify
        poll = self.poll
        
        e = poll()
        while e.type != NOEVENT:
            notify(e)
            e = poll()

AbstractEQM = AbstractEventQueueManager

class SysEventQueueManager(AbstractEQM):
    """ An event queue manager that uses the system event queue.
    
        ~~~ Usage ~~~
    
        eqm = SysEventQueueManager([ child, child, ...])
        
        or
        
        eqm = SysEQM([ child, child, ...])
        
        ~~~ Description ~~~
    
        SysSEQM does the exact same thing as EQM, except that instead of
        using a Python Queue.Queue object it uses Pygame's event queue,
        called the "system queue". This has two benefits:
        
        1.) Pygame's system event queue is written in C, making it
            faster than a Python-implemented event queue.
        
        2.) Events that come from the system (KEYUP, MOUSEMOTION, etc.)
            will automatically appear in the manager's queue as a
            side-effect of it sharing its event queue with Pygame.
            
        The .post(), .get(), and .poll() methods of SysEQM are
        actually just staticmethod wrappers around the corresponding
        functions of pygame.event. These methods can be used in the
        same way as their corresponding Pygame functions.
        
        Note that SysEQM doesn't have a .queue member like EQM does;
        this is because events come from Pygame functions rather than
        any class instance.
        
        See the AbstractEQM and EQM classes for more details.
        
        ~~~ Class Hierarchy ~~~
        
        Methods are resolved in the following order:
        
        sbak.event.SysEventQueueManager
            sbak.event.AbstractEventQueueManager
                sbak.event.EventManager
                    sbak.event.Forwarder
                        sbak.tree.Branch
                            sbak.tree.Leaf
                    sbak.event.Responder
                        sbak.tree.Leaf
    """
    
    post = staticmethod(post)
    get = staticmethod(get)
    poll = staticmethod(poll)

SysEQM = SysEventQueueManager

class EventQueueManager(AbstractEventQueueManager):
    """ Queues events for asynchronous event handling through hooks.
    
        ~~~ Usage ~~~
        
        eqm = EventQueueManager( maxevents, [ child, child, ... ])
        
        or
        
        eqm = EQM( maxevents, [ child, child, ... ])
        
        ~~~ Description ~~~
        
        TODO: More info!!!
        
        Events are managed using a Python queue (a Queue.Queue object).
        This queue can be accessed directly through the `.queue` member
        of the eqm object.
        
        Some frequently used methods of the queue can also be accessed
        through members of the EventManager object:
        
        * `manager.poll()` calls `manager.queue.get_nowait()` and
          ignores QueueEmpty errors, and
          
        * `manager.post()` calls `manager.queue.put_nowait()`, but
          does NOT ignore QueueFull errors.
          
        ~~~ Class Hierarchy ~~~
        
        Methods are resolved in the following order:
        
        sbak.event.EventQueueManager
            sbak.event.AbstractEventQueueManager
                sbak.event.EventManager
                    sbak.event.Forwarder
                        sbak.tree.Branch
                            sbak.tree.Leaf
                    sbak.event.Responder
                        sbak.tree.Leaf
    """
    
    def __init__(self, maxevents=0, children=(), id = None):
        AbstractEQM.__init__(self, children, id = id)
        self.queue = Queue(maxevents)
    
    def poll(self):
        """ Return and remove one event from the manager's own queue.
            
            Returns an Event object (or it should, anyway). If the
            queue is empty, an Event with type NOEVENT is returned.
        """
        try:
            return self.queue.get_nowait()
        except QueueEmpty:
            return _noevent
    
    def get(self):
        """ Returns and removes all events in the queue.
        
            This method may be slow because it must repeatedly call the
            .get_nowait() method on the eqm's queue.
            (NOTE: SysEQM.get is much faster because its
            actually just a wrapper around pygame.event.get.)
        
            Returns a list with 0 or more events.
        """
        get_nowait = self.queue.get_nowait
        L = []
        append = L.append
        try:
            while True:
                append(get_nowait())
        except QueueEmpty:
            return L
    
    def post(self, e):
        """ Put one event on the eqm's own queue.
        
            Equivalent to eqm.queue.put_nowait( e )
        """
        self.queue.put_nowait(e)

EQM = EventQueueManager

class Hook(sbak.base.Object):
    """ A binding between a callable and an event description.
    
        An instance of this class is created for every event handler
        hooked to an event. When the event occurs and the hook is
        triggered, the handler is passed the instance. During that
        call, the Hook instance will act as a psuedo-proxy to the Event
        object that triggered the callback and will also provide some
        information about itself.
        
        ~~~ Class Attributes ~~~
        
        is_a_hook:  This exists simply to distinguish a Hook object
                    from an Event object. When an event handler is
                    called, one can determine whether or not the
                    argument is a hook by checking for this atrribute
                    using the Python built-in getattr() function. If
                    the attribute exists, the object is probably a
                    hook. (Or else some idiot gave an Event object an
                    attribute called "is_a_hook" just to make me
                    angry... :D) If not, it is probably an Event.
                    This only needs to be checked if hook-specific
                    features are going to be used and it is known that
                    the handler may receive either Hooks OR events.
        
        ~~~ Instance Attributes ~~~
        
        func:   The callback that the hook calls when it is triggered.
        
        type:   The type of event that triggers this hook.
        
        state:  The state (attribute values as dict) that an event is
                required to have in order to trigger the hook. This may
                be an empty dict, in which case all events of the
                correct type will trigger the hook regardless of state.
        
        priority:
                The priority of the hook in relevence to other hooks
                on the same event. Multiple hooks on the same event
                will be triggered in order from lowest to highest
                priority number. Negatives and floats are allowed,
                but only integers are recomended.
        
        event:  When the Hook is passed to a triggered callback, this
                is the event that triggered the hook. Otherwise, this
                is None.
        
        on_remove:
                A list of callbacks to call when the hook is removed.
                Add callables to this list to have them called when
                the hook is removed. This can be used, for example, to
                notify the creator of a hook so that it can drop any
                references to it. After these are called, the list is
                emptied.
        
        Reads of nonexisting attributes are deffered first to the
        `event` object (if it exists), then as keys to the `state`
        object.
        
        The Hook object has a method, remove() which
        can be called to remove the hook from its responder.
        
        The hook object may be called with a single argument, an Event
        object. This will call the hook's callback. While the callback
        is being called, the event passed to the hook can be accessed
        via the `event` attribute.
    """
    
    is_a_hook = True
    
    def __init__(self, func, type, priority=0, state=None, **kwstate):
        """ Initialize a Hook object.
        
            func:   The function to call.
            
            type:   The event type number.
            
            priority:
                    Determines order of occurence for multiple
                    hooks bound to the same event. Hooks are triggered
                    in order of lowest to highest priority.
            
            state:  The state of the event. (Checks for equality only.)
            
            As an alternative to the `state` argument you can pass a
            series of keyword arguments (**kwstate).    
        """
        # Combine state and kwstate. Null state will be converted to dict.
        state = dict(state or {})
        state.update(kwstate)
        
        # Fix passed state and type
        type = _fix_state(type, state)
        
        # Store given values
        self.type, self.state, self.func, self.priority = (
             type,      state,      func,      priority    )
        
        # Other defaults
        self._rsp, self.on_remove, self.event = None, [], None
    
    def __repr__(self):
        return "<Hook(func = %(func)s, type = \"%s(type)s\", priority = "\
               "%(priority)s%(state)s)%(event)s>" % {
                    'func' : self.func,
                    'type' : '"' + _ename[self.type] + '"',
                    'priority' : self.priority,
                    'state' : self.state,
                    'event' : (
                        ('; event = %s' % self.event)
                        if self.event is not None
                        else '')
                }
    
    def __str__(self):
        return "<Hook %s -> %s>"%(self.func, repr(_ename[self.type]))
    
    def __getattr__(self,attr):
        try:
            return getattr(self.event, attr)
        except AttributeError:
            if attr in self.state:
                return self.state[attr]
            raise
        
    def __call__(self,event):
        self.event = event
        r = self.func(self)
        self.event = None
        return r
    
    def _set_rsp(self, rsp):
        """ Internal function. Do not use.
        
            This simply sets the responder for a hook. However,
            hooks are not supposed to change to another responder
            after creation, so don't call this yourself.
        """
        self._rsp = rsp
    
    responder = property(
        fget = attrgetter('_rsp'),
        doc =
        """ The responder to which the hook belongs.
            None if the hook is not assigned to any responder.
            Read-only.
        """
    )
    
    def unhook(self):
        """ Call this to remove the hook from its responder.
            
            This will also call the hook's `on_unhook` psuedoevent
            handlers and empty the `on_remove` handler list.
        """
        self._rsp._unhook_hook(self)
        self._set_rsp(None)
        for c in self.on_unhook:
            c(self)
        self.on_unhook[:] = []
